Skip to content
created by Aha00aAha00a at 2026-08-10
last modified by Aha00aAha00a at 2026-09-15
revision: 6

Dev ApiControllers

/api 아래 endpoint 를 어느 컨트롤러가 갖는지, 그리고 왜 그렇게 나눴는지.

1. 경계

나누는 기준은 **누가 부르는가** 다. 파일 크기가 아니다.

컨트롤러

무엇을 갖는가

Api

위키 자신이 부르는 것 — 현재 사용자, CSRF 토큰, 페이지 이름·미리보기, 링크, 편집기 자동완성 목록, 전체 통계

ApiAdminSite

**한 사이트를 바꾸는 것** — 사이트 레코드, 권한, 사이트 관리자, favicon·테마·텔레그램, 페이지 메타, 캐시 재계산

ApiAdminReport

**여러 사이트를 걸쳐 읽는 것** — 사용자 목록, 조회 이력, 일별 통계, 인기 페이지, 최근 변경, 접근 로그

ApiAdminS3

버킷 브라우저 — 목록·삭제·다운로드 URL

ApiApiKey

API Key — 사용자가 자기 것을 관리하는 쪽과 관리자가 회수하는 쪽

ApiUserNickname

닉네임 변경 요청 — 사용자가 신청·철회하는 쪽과 관리자가 승인·거절하는 쪽

ApiV1

Bearer 인증 외부 API. Dev Api 참고

ApiCrawler

외부 URL 크롤링과 그 캐시

위키 페이지 자체를 다루는 컨트롤러도 같은 기준으로 나뉘어 있습니다.

컨트롤러

무엇을 갖는가

Wiki

페이지를 읽고 쓰는 것 — view, save, delete, rename, preview 와 렌더링·권한 요약 헬퍼

WikiAttachment

첨부 — 업로드·목록·삭제·클립보드 붙여넣기. S3 에 바이트를 넣는 쪽

WikiRealtime

페이지 WebSocket — 누가 보고 있는지, 커서 위치, 저장 알림

WikiSpreadsheet

Google Spreadsheet 를 표로 끌어오는 것. 남의 서비스와의 연동

Wiki 에서 렌더링 헬퍼가 나가지 않은 이유는 view 와 preview 가 같은 방식으로 렌더링하기 때문이고, 권한 헬퍼가 남은 이유는 view 가 요약을 보여주고 save 가 그것을 적용하기 때문입니다.

save 는 여전히 logics.PageCursorHub 를 호출해 변경을 알립니다. 이 hub 는 WebSocket 이 소유한 것이 아니라 **공유하는 것** 이라, 둘이 떨어져도 각자 절반의 구독자만 갖는 일이 생기지 않습니다.

ApiAdminSite 에서 URL 에 :seq 를 갖는 endpoint 는 withSiteAdmin 또는 withAdminSite 를 지납니다. 그 둘이 **권한을 먼저 보고 그 다음에 site 를 조회** 하는 순서를 쥐고 있어서, 순서가 갈라지지 않도록 한 곳에 모았습니다. favicon·테마처럼 siteSeq 를 쿼리·폼 파라미터로 받는 다섯 endpoint 는 resolveAdminTargetSiteWithAuth 를 거치고, 순서는 같습니다. 자세한 이유와 그 다섯이 한때 반대 순서였던 사정은 Dev ApiResponse 참고.

ApiAdminReport 는 읽기 전용 질의만 있습니다. 느려질 가능성이 가장 큰 endpoint 들이라, 한 파일에 모으면 어디를 봐야 하는지가 드러납니다.

2. 예외 셋

Api 에 관리 화면이 부르는 endpoint 세 개가 남아 있습니다. 둘은 이유가 있고, 하나는 옮겨지지 않았습니다.

  • adminGenerateSignedReadUrl — 사이트가 아니라 **페이지** 에 대한 것입니다. 사이트 관리와 성격이 다릅니다.
  • adminMemoryCacheStats — 이 값을 쓰는 주기 작업이 Api 생성자에 등록돼 있습니다. 같은 캐시 키를 쓰는 **읽는 쪽과 쓰는 쪽** 이라 떨어뜨리면 키가 갈라집니다.
  • cacheDelete(DELETE /api/cache/:siteSeq) — 관리 화면의 사이트 캐시 삭제가 부릅니다. 한 사이트의 캐시를 비우는 일이라 ApiAdminSite 의 몫인데, 나눌 때 옮기지 않았습니다. 2026-09-15 까지는 권한 검사도 없어서 누구나 비울 수 있었고, 지금은 그 사이트의 관리자만 비웁니다.

3. 나눌 때 지킨 것

  • 라우트만 바꾸고 endpoint 의 동작은 건드리지 않았습니다.
  • 옮긴 뒤 conf/routes 의 모든 참조가 실재하는 컨트롤러·메서드를 가리키는지 기계로 확인했습니다. 라우트는 문자열이라 오타가 컴파일에 걸리지 않습니다.
  • 여러 컨트롤러가 함께 쓰는 것은 트레이트로 갑니다 — JsonResults, AdminAuth. 컨트롤러를 나누면 헬퍼를 복사하고 싶어지는데, 그 순간이 사본이 둘이 되는 순간입니다. withLoginUser(로그인 안 했으면 401 JSON)가 바로 그렇게 ApiApiKeyApiUserNickname 에 똑같이 복사돼 있었고, 2026-09-15 에 JsonResults 로 합쳤습니다.

4. See Also

4.2. Similar Pages

Similar pages by cosine similarity. Words after page name are term frequency.

  • 59.95% Dev Api api(24:147), key(3:85), user(3:44), page(1:33), admin(12:20), wiki(5:22), v1(1:19), csrf(1:14), endpoint(6:7), 페이지(6:5)
  • 56.85% Dev ApiResponse api(24:42), admin(12:33), site(9:36), json(3:42), seq(3:9), with(4:7), endpoint(6:4), wiki(5:5), 같은(3:7), crawler(1:8)
  • 54.15% Api api(24:84), key(3:32), page(1:27), 페이지(6:19), wiki(5:15), v1(1:16), 저장(1:15), admin(12:3), 조회(2:11), bearer(1:11)
  • 50.14% Dev AdminUIRoleMenu api(24:8), admin(12:14), site(9:14), seq(3:5), wiki(5:1), url(4:2), 사이트(4:2), cache(3:3), user(3:3), dev(3:1)
  • 47.16% Dev SiteAdmin admin(12:25), site(9:27), api(24:5), seq(3:6), user(3:5), 사이트(4:3), 캐시(4:2), with(4:1), delete(3:1), dev(3:1)
  • 35.04% Dev Cache cache(3:58), site(9:35), wiki(5:33), api(24:12), 캐시(4:29), memory(1:25), page(1:20), admin(12:8), seq(3:17), 페이지(6:8)
  • 33.12% ToDo-User-Nickname-Change user(3:59), nickname(2:55), api(24:31), admin(12:21), 닉네임(1:20), seq(3:14), 같은(3:12), 관리자(1:12), key(3:9), 현재(1:11)

4.3. Adjacent Pages

Control
≤ 32
all
1.0x
1.0x
80
-120
ON
Metrics
Nodes(visible/total)0/0
Links(visible/total)0/0
Avg degree0.00
Depth coverage0
Queue(fetch/graph)0 / 0
Zoom(scale)1.00x
Ctrl/⌘ + Scroll: Zoom
Root 1-hop 2-hop+